/*!
* \file main.cc
* \brief Main file of the GNSS-SIM program.
* \author Javier Arribas, 2015. jarribas(at)cttc.es
*
* It sets up the logging system, creates a the main thread,
* makes it run, and releases memory back when the main thread has ended.
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2015 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems Simulator
*
* This file is part of GNSS-SIM.
*
* GNSS-SIM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GNSS-SIM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SIM. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#ifndef GNSS_SIM_VERSION
#define GNSS_SIM_VERSION "0.0.1"
#endif

#ifndef GOOGLE_STRIP_LOG
#define GOOGLE_STRIP_LOG 0
#endif
#include "simulator_control.h"
#include <boost/exception/diagnostic_information.hpp>
#include <boost/exception_ptr.hpp>
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
#include <gflags/gflags.h>
#include <glog/logging.h>
#include <iostream>

#if CUDA_GPU_ACCEL
// For the CUDA runtime routines (prefixed with "cuda_")
#include <cuda_runtime.h>
#endif

using google::LogMessage;

DECLARE_string(log_dir);
DEFINE_string(obs_pos_file, "circle.csv", "Observer positions file, in .csv or .nmea format");
DEFINE_string(rinex_nav_file, "RINEX.NAV", "Satellite ephemeris file in RINEX 2.10 navigation format");
DEFINE_string(rinex_obs_file, "sim.15o", "RINEX 2.10 observation file output");
DEFINE_string(sig_out_file, "signal_out.bin", "Baseband signal output file. Will be stored in int8_t IQ multiplexed samples");
DEFINE_double(sampling_freq, 2.6e6, "Baseband sampling frequency [Hz]");
DEFINE_string(static_position, "", "Lat,Lon,Hgt,N_Points (static mode) e.g. 30.286502,120.032669,100,1000");
DEFINE_double(CN0_dBHz, std::numeric_limits<double>::infinity(), "Carrier-to-Noise ratio (CN0) [dB-Hz]");

int main(int argc, char** argv)
{
    const std::string intro_help(
        std::string("\nGNSS-SIM is an Open Source GNSS Software Defined Simulator\n") +
        "Copyright (C) 2015 (see AUTHORS file for a list of contributors)\n" +
        "This program comes with ABSOLUTELY NO WARRANTY;\n" +
        "See COPYING file to see a copy of the General Public License\n \n");

    const std::string gnss_sdr_version(GNSS_SIM_VERSION);
    google::SetUsageMessage(intro_help);
    google::SetVersionString(gnss_sdr_version);
    google::ParseCommandLineFlags(&argc, &argv, true);

    std::cout << "Initializing GNSS-SIM v" << gnss_sdr_version << " ... Please wait." << std::endl;

#if CUDA_GPU_ACCEL
    // Reset the device
    // cudaDeviceReset causes the driver to clean up all state. While
    // not mandatory in normal operation, it is good practice.  It is also
    // needed to ensure correct operation when the application is being
    // profiled. Calling cudaDeviceReset causes all profile data to be
    // flushed before the application exits
    cudaDeviceReset();
    std::cout << "Reset CUDA device done " << std::endl;
#endif

    if (GOOGLE_STRIP_LOG == 0)
        {
            google::InitGoogleLogging(argv[0]);
            if (FLAGS_log_dir.empty())
                {
                    std::cout << "Logging will be done at "
                              << boost::filesystem::temp_directory_path()
                              << std::endl
                              << "Use gnss-sdr --log_dir=/path/to/log to change that."
                              << std::endl;
                }
            else
                {
                    const boost::filesystem::path p(FLAGS_log_dir);
                    if (!boost::filesystem::exists(p))
                        {
                            std::cout << "The path "
                                      << FLAGS_log_dir
                                      << " does not exist, attempting to create it."
                                      << std::endl;
                            boost::system::error_code ec;
                            if (!boost::filesystem::create_directory(p, ec))
                                {
                                    std::cout << "Could not create the " << FLAGS_log_dir << " folder. GNSS-SDR program ended." << std::endl;
                                    google::ShutDownCommandLineFlags();
                                    std::exit(0);
                                }
                        }
                    std::cout << "Logging with be done at " << FLAGS_log_dir << std::endl;
                }
        }


    std::unique_ptr<simulator_control> sim_ctl(new simulator_control());

    // record startup time
    struct timeval tv
    {
    };
    gettimeofday(&tv, nullptr);
    long long int begin = tv.tv_sec * 1000000 + tv.tv_usec;
    try
        {
            // READ command line parameters and initialize files
            // Observer coordinates
            if (FLAGS_static_position.size() < 10)
                {
                    //dynamic mode

                    if (FLAGS_obs_pos_file.find(".csv") != std::string::npos)
                        {
                            if (sim_ctl->readUserMotion(FLAGS_obs_pos_file) == -1)
                                {
                                    std::cout << "Problem reading " << FLAGS_obs_pos_file << " file" << std::endl;
                                }
                        }
                    else if (FLAGS_obs_pos_file.find(".nmea") != std::string::npos)
                        {
                            if (sim_ctl->readNmeaGGA(FLAGS_obs_pos_file) == -1)
                                {
                                    std::cout << "Problem reading " << FLAGS_obs_pos_file << " file" << std::endl;
                                }
                        }
                    else
                        {
                            std::cout << "Unknown observer position file format. Please use .csv or .nmea files." << std::endl;
                        }
                }
            else
                {
                    //static mode
                    double llh[3];
                    int n_points;
                    sscanf(FLAGS_static_position.c_str(), "%lf,%lf,%lf,%d", &llh[0], &llh[1], &llh[2], &n_points);
                    sim_ctl->setstaticposition(llh[0], llh[1], llh[2], n_points);
                    std::cout << " GNSS SIM will run in static observer mode with Lat=" << llh[0] << " Long=" << llh[1] << " Height=" << llh[2] << " and " << n_points << " observation epochs" << std::endl;
                }

            // Output signal file
            if (sim_ctl->open_out_file(FLAGS_sig_out_file) == false)
                {
                    std::cout << "Problem creating signal output file " << FLAGS_sig_out_file << std::endl;
                }

            // Ephemeris file

            if (sim_ctl->read_eph(FLAGS_rinex_nav_file) == false)
                {
                    std::cout << "Problem reading RINEX epehemris file " << FLAGS_rinex_nav_file << std::endl;
                }

            if (sim_ctl->setsamplingfreq(FLAGS_sampling_freq) == false)
                {
                    std::cout << "Sampling frequency not valid!" << std::endl;
                }

            if (sim_ctl->open_rinex_obs_file(FLAGS_rinex_obs_file) == false)
                {
                    std::cout << "Problem creating signal RINEX Observation output file " << std::endl;
                }

            //Set CN0
            sim_ctl->setCN0(FLAGS_CN0_dBHz);

            std::cout << "Running simulator..." << std::endl;

            sim_ctl->run();
        }
    catch (boost::exception& e)
        {
            LOG(FATAL) << "Boost exception: " << boost::diagnostic_information(e);
        }
    catch (std::exception const& ex)
        {
            LOG(FATAL) << "STD exception: " << ex.what();
        }
    catch (...)
        {
            LOG(INFO) << "Unexpected catch";
        }


    // report the elapsed time
    gettimeofday(&tv, nullptr);
    long long int end = tv.tv_sec * 1000000 + tv.tv_usec;
    std::cout << std::endl;
    std::cout << "Total GNSS-SIM run time "
              << (static_cast<double>(end - begin)) / 1000000.0
              << " [seconds]" << std::endl;

    google::ShutDownCommandLineFlags();
    std::cout << "GNSS-SIM program ended." << std::endl;
    return 0;
}
